/************************************************************
 * Copyright (C), 2022-2030, dina. Co., Ltd.
 * FileName:        cycle_buffer.h
 * Author:          Dan Yin
 * Date:            2022-11-23
 * Description:     the class of buffer for serialport
 * Version:         1.0.0.0
 **********************************************************/

#ifndef CYCLE_BUFFER_H
#define CYCLE_BUFFER_H

#include <stddef.h>
#include <stdlib.h>
#include <mutex>
//#include <QDebug>

#ifndef DEF_BUF_MIN
#define _min_(a,b) (((a) < (b)) ? (a) : (b))
#endif

#ifndef DEF_BUF_MAX
#define _max_(a,b) (((a) > (b)) ? (a) : (b))
#endif

#define DF_BUF_SIZE     4 * 1024

template<class _value_type>
class CycleBuffer
{
public:
    CycleBuffer(int buf_size = DF_BUF_SIZE);
    ~CycleBuffer();

    void clear(){ _r = 0; _w = 0;}
    unsigned int capacity() const { return _buffer_size; }
    unsigned int size();
    unsigned int available();
    bool empty();
    bool full(unsigned int append = 0);

    unsigned int write(_value_type* buffer, unsigned int len);
    unsigned int read(_value_type* buffer, unsigned int len);

    bool pop_front(_value_type& value);
    bool pop_front(void);
    bool push_back(_value_type& value);
    bool push_front(_value_type& value);
    bool resize(int newSize);

    _value_type& front(void) const { return _buffer[_r % _buffer_size]; }
    _value_type& operator[](size_t offset) { return _buffer[(_r + offset) % _buffer_size]; }

protected:
   inline unsigned int _max(unsigned int a, unsigned int b) { return _max_(a, b); }
   inline unsigned int _min(unsigned int a, unsigned int b) { return _min_(a, b); }

private:
   std::mutex               mtx_read;
   std::mutex               mtx_write;
   int                      _buffer_size;
   _value_type              *_buffer;
   volatile unsigned int    _r;
   volatile unsigned int    _w;
};

template<class _value_type>
CycleBuffer<_value_type>::CycleBuffer(int buf_size)
      : _buffer_size(buf_size), _buffer(NULL), _r(0), _w(0)
{
    if(DF_BUF_SIZE > _buffer_size) _buffer_size = DF_BUF_SIZE;

    _buffer = new _value_type[_buffer_size];
    memset(_buffer, 0, _buffer_size * sizeof(_value_type));
}

template<class _value_type>
CycleBuffer<_value_type>::~CycleBuffer()
{
    if (_buffer)  delete[] _buffer;
    _buffer = NULL;
}

template<class _value_type>
unsigned int CycleBuffer<_value_type>::size()
{
    return (_w >= _r) ? _w - _r : _buffer_size - (_r - _w);
}

template<class _value_type>
unsigned int CycleBuffer<_value_type>::available()
{
    return _buffer_size - size();
}

template<class _value_type>
bool CycleBuffer<_value_type>::empty() {
    return 0 == size();
}

template<class _value_type>
bool CycleBuffer<_value_type>::full(unsigned int append) {
    return (int)_buffer_size <= (int)(size() + append);
}

template<class _value_type>
unsigned int CycleBuffer<_value_type>::write(_value_type* buffer, unsigned int len)
{
    mtx_write.lock();

    if(!full(len)) {
        unsigned int offset = _w % _buffer_size;
        unsigned int tail = _buffer_size - offset;

        if(len <= tail) {
            memcpy(_buffer + offset , buffer, len * sizeof(_value_type));
        }
        else {
            // first put the data starting from fifo->in to buffer end
            memcpy(_buffer + offset , buffer, tail * sizeof(_value_type));

            // then put the rest (if any) at the beginning of the buffer
            memcpy(_buffer, buffer + tail, (len - tail) * sizeof(_value_type));
        }

//        qDebug() << Q_FUNC_INFO << "-----------------w" << _w << _r << len << tail << offset << _buffer_size;

        _w  = (_w + len) % _buffer_size;

        mtx_write.unlock();
        return len;
    }
    else {
        mtx_write.unlock();
        return 0;
    }
}

template<class _value_type>
 unsigned int CycleBuffer<_value_type>::read(_value_type* buffer, unsigned int len)
{
     mtx_read.lock();

     if(size() >= len) {
         unsigned int offset = _r % _buffer_size;
         unsigned int tail = _buffer_size - offset;

         if(len <= tail) {
             memcpy(buffer, _buffer + offset, len * sizeof(_value_type));
         }
         else {
             // first put the data starting from fifo->in to buffer end
             memcpy(buffer, _buffer + offset, tail * sizeof(_value_type));

             // then put the rest (if any) at the beginning of the buffer
             memcpy(buffer + tail, _buffer, (len - tail) * sizeof(_value_type));
         }

//         qDebug() << Q_FUNC_INFO << "-----------------r" << _w << _r << len << tail << offset << _buffer_size;
         _r  = (_r + len) % _buffer_size;

         mtx_read.unlock();
         return len;
     }
     else {
         mtx_read.unlock();
         return 0;
     }
}

template<class _value_type>
bool CycleBuffer<_value_type>::pop_front(_value_type& value)
{
    mtx_read.unlock();

    if(empty()) {
        mtx_read.unlock();
        return false;
    }
    else {
        value = _buffer[_r];

//        qDebug() << Q_FUNC_INFO << "^^^^^^^^^^^^^^^^^p" << _w << _r << _buffer_size;
        _r = (_r + 1) % _buffer_size;

        mtx_read.unlock();
        return true;
    }
}

template<class _value_type>
bool CycleBuffer<_value_type>::pop_front(void)
{
    mtx_read.unlock();

    if(empty()) {
        mtx_read.unlock();
        return false;
    }
    else {
//        qDebug() << Q_FUNC_INFO << "*******************p" << _w << _r << _buffer_size;
        _r = (_r + 1) % _buffer_size;

        mtx_read.unlock();
        return true;
    }
}

template<class _value_type>
bool CycleBuffer<_value_type>::push_back(_value_type& value)
{
    mtx_write.lock();

    if(full()) {
        mtx_write.unlock();
        return false;
    }
    else {
        _w = (_w + 1) % _buffer_size;
        _buffer[_w] = value;

        mtx_write.unlock();
        return true;
    }
}

template<class _value_type>
bool CycleBuffer<_value_type>::push_front(_value_type& value)
{
    mtx_write.lock();

    if(full()) {
        mtx_write.unlock();
        return false;
    }
    else {
        _r = (_r - 1) % _buffer_size;
        _buffer[_r] = value;

        mtx_write.unlock();
        return true;
    }
}

template<class _value_type>
bool CycleBuffer<_value_type>::resize(int new_size)
{
    size_t old_size = size();

    if( (old_size > new_size) || (DF_BUF_SIZE > new_size)) {
        return false;
    }

    _value_type *new_value_ptr = new _value_type[new_size];
    if(new_value_ptr) {
        memset(new_value_ptr, 0, new_size * sizeof(_value_type));

        if(_buffer) {
            if(0 < old_size) read(new_value_ptr, old_size);
            delete[] _buffer;
            _buffer = NULL;
        }

        _buffer = new_value_ptr;
        _buffer_size = new_size;

        mtx_read.lock();
        _r = 0;
        mtx_read.unlock();

        mtx_write.lock();
        _w = old_size;
        mtx_write.unlock();

        return true;
    }
    else {
        return false;
    }
}

#endif // CYCLE_BUFFER_H
